/**
 * 
 */
package fr.gfi.agraf.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import fr.gfi.agraf.exception.AgrafException;
import fr.gfi.agraf.persistence.dao.IProfilDAO;
import fr.gfi.agraf.persistence.dao.ITacheDAO;
import fr.gfi.agraf.persistence.dao.IUtilisateurDAO;
import fr.gfi.agraf.persistence.entity.Profil;
import fr.gfi.agraf.persistence.entity.Tache;
import fr.gfi.agraf.persistence.entity.Utilisateur;
import fr.gfi.agraf.service.IUtilisateurService;
import fr.gfi.agraf.service.mapper.UtilisateurMapper;
import fr.gfi.agraf.utils.CryptageUtils;
import fr.gfi.agraf.utils.GenerationUtils;
import fr.gfi.agraf.utils.PropertiesUtils;
import fr.gfi.agraf.utils.ThreadEnvoiMail;
import fr.gfi.agraf.vue.bean.ProfilDTO;
import fr.gfi.agraf.vue.bean.UtilisateurDTO;

/**
 * @author Zakaria
 * 
 */
@Service("UtilisateurService")
@Transactional(propagation = Propagation.REQUIRED)
public class UtilisateurService implements IUtilisateurService {

	private static final String ERREUR_UTILISATEUR_LOGIN_EXIST = "erreur.utilisateur.loginExist";

	private static final String ERREUR_UTILISATEUR_CONNECTE = "erreur.utilisateur.connecte";

	private static final String ERREUR_UTILISATEUR_TACHE_ENCOURS = "erreur.utilisateur.tache.encours";

	private static final String ERREUR_UTILISATEUR_NOTFOUND = "erreur.utilisateur.notfound";

	private static final String ERREUR_EMAIL_EXIST = "erreur.email.exist";

	private static final String ERREUR_PASSWORD = "erreur.password";

	private static final String ERREUR_UTILISATEUR_NOTEXIST = "erreur.utilisateur.notexist";

	private static final String CONTENU_MAIL_NOUVEAU_MDP = "contenu.mail.nouveau.mdp";

	private static final String OBJET_MAIL_NOUVEAU_MDP = "objet.mail.nouveau.mdp";

	@Autowired
	private IUtilisateurDAO utilisateurDAO;

	@Autowired
	private ITacheDAO tacheDAO;

	@Autowired
	private IProfilDAO profilDAO;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#isMailExistant(java.lang.String)
	 */
	public boolean traiterDemandeMdpOublie(final String email) {
		// Récupération de l'utilisateur concerné
		final Utilisateur utilisateurRecup = utilisateurDAO.getUtilisateurByEmail(email);
		if (utilisateurRecup == null) {
			return false;
		}

		// Je genere le nouveau mot de passe
		final String nouveauMdp = GenerationUtils.genererMdp(10);

		// Je genere le contenu du mail a envoyer
		final String contenuMail = PropertiesUtils.getProperty(CONTENU_MAIL_NOUVEAU_MDP, nouveauMdp);

		// Je persist le nouveau mot de passe
		utilisateurRecup.setMotDePasse(CryptageUtils.crypterString(nouveauMdp));
		utilisateurDAO.update(utilisateurRecup);

		// J'envoi le mail dans un thread
		final String objetMail = PropertiesUtils.getProperty(OBJET_MAIL_NOUVEAU_MDP);
		final Thread th = new Thread(new ThreadEnvoiMail(utilisateurRecup.getEmail(), objetMail, contenuMail));
		th.start();

		return true;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#getUtilisateurParIdentification
	 * (java.lang.String, java.lang.String)
	 */
	public UtilisateurDTO getUtilisateurParIdentification(final String login, final String password)
			throws AgrafException {
		// Appel au DAO
		final Utilisateur utilisateur = utilisateurDAO.getUtilisateurByIdentification(login,
				CryptageUtils.crypterString(password));
		// Si l'utilisateur n'est pas trouvé, genere une exception
		if (utilisateur == null) {
			throw new AgrafException(ERREUR_UTILISATEUR_NOTEXIST);
		}
		// Convertit l'entité en DTO et la retourne
		return UtilisateurMapper.entityToDTO(utilisateur);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#modifierUtilisateur(fr.gfi.agraf
	 * .service.beans.UtilisateurDTO)
	 */
	public UtilisateurDTO modifierUtilisateur(final UtilisateurDTO utilisateurDTO, final boolean fromAdmin)
			throws AgrafException {

		final String newMotDePasse = utilisateurDTO.getNewMotDePasse();
		final String newEmail = utilisateurDTO.getNewEmail();
		// récupére l'utilisateur
		final Utilisateur utilisateur = utilisateurDAO.getUtilisateurActifById(utilisateurDTO.getId());
		if (utilisateur == null) {
			throw new AgrafException(ERREUR_UTILISATEUR_NOTFOUND);
		}
		// vérifie que le mot de passe est correct dans le cas ou on est pas en
		// administration
		if (!fromAdmin
				&& !StringUtils.equals(utilisateur.getMotDePasse(),
						CryptageUtils.crypterString(utilisateurDTO.getMotDePasse()))) {
			throw new AgrafException(ERREUR_PASSWORD);
		}
		// vérifie s'il y a un nouveau mot de passe
		if (StringUtils.isNotBlank(newMotDePasse)) {
			// crypte et met le nouveau mot de passe
			final String newPasswordCrypte = CryptageUtils.crypterString(newMotDePasse);
			utilisateur.setMotDePasse(newPasswordCrypte);
		}
		// vérifie s'il y a un nouveau mail
		verifieEtAjouteMail(utilisateur, newEmail);

		// verifie s'il y a un nouvel identifiant
		final String newIdentifiant = utilisateurDTO.getNewIdentifiant();
		if (!StringUtils.equals(newIdentifiant, utilisateur.getIdentifiant())) {
			verifieEtAjouteIdentifiant(utilisateur, newIdentifiant);
		}
		// verifie s'il y a un nouveau nom
		final String nom = utilisateurDTO.getNom();
		if (StringUtils.isNotBlank(nom)) {
			utilisateur.setNom(nom);
		}
		// verifie s'il y a un nouveau prenom
		final String prenom = utilisateurDTO.getPrenom();
		if (StringUtils.isNotBlank(prenom)) {
			utilisateur.setPrenom(prenom);
		}
		return UtilisateurMapper.entityToDTO(utilisateur);
	}

	/**
	 * @param newEmail
	 * @param utilisateur
	 * @throws AgrafException
	 */
	private void verifieEtAjouteMail(final Utilisateur utilisateur, final String newEmail)
			throws AgrafException {
		if (StringUtils.isNotBlank(newEmail) && !utilisateur.getEmail().equals(newEmail)) {
			final Utilisateur checkMailExist = utilisateurDAO.getUtilisateurByEmail(newEmail);
			// verifie si un utilisateur utilise deja ce mail
			if (checkMailExist == null) {
				// met le nouveau mail
				utilisateur.setEmail(newEmail);
			} else {
				throw new AgrafException(ERREUR_EMAIL_EXIST);
			}
		}
	}

	/**
	 * @param utilisateur
	 * @param newIdentifiant
	 * @throws AgrafException
	 */
	private void verifieEtAjouteIdentifiant(final Utilisateur utilisateur, final String newIdentifiant)
			throws AgrafException {
		if (StringUtils.isNotBlank(newIdentifiant)) {
			// verifie si l'identifiant existe deja sinon l'ajoute
			final Utilisateur checkLoginExist = utilisateurDAO.getUtilisateurByIdentifiant(newIdentifiant);
			if (checkLoginExist == null) {
				// met le nouvel identifiant
				utilisateur.setIdentifiant(newIdentifiant);
			} else {
				throw new AgrafException(ERREUR_UTILISATEUR_LOGIN_EXIST);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.gfi.agraf.service.IUtilisateurService#getAllUtilisateurActif()
	 */
	public List<UtilisateurDTO> getAllUtilisateurActif() throws AgrafException {
		// récupere la liste des utilisateurs qui ont une date de sortie à null
		// ou > today
		final List<Utilisateur> utilisateurList = utilisateurDAO.getAllUtilisateurActif();
		if (CollectionUtils.isEmpty(utilisateurList)) {
			throw new AgrafException(ERREUR_UTILISATEUR_NOTFOUND);
		}
		// map en DTO et retourne
		return UtilisateurMapper.entityToDTO(utilisateurList);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see fr.gfi.agraf.service.IUtilisateurService#getAllUtilisateurInactif()
	 */
	public List<UtilisateurDTO> getAllUtilisateurInactif() throws AgrafException {
		// récupere la liste des utilisateurs qui ont une date de sortie
		final List<Utilisateur> utilisateurList = utilisateurDAO.getAllUtilisateurInactif();
		// map en DTO et retourne
		return UtilisateurMapper.entityToDTO(utilisateurList);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#supprimerUtilisateur(java.lang
	 * .Long)
	 */
	public void supprimerUtilisateur(final Long id, final Long idConnecte) throws AgrafException {
		if (id == idConnecte) {
			throw new AgrafException(ERREUR_UTILISATEUR_CONNECTE);
		}
		// récupére l'utilisateur
		final Utilisateur utilisateur = utilisateurDAO.getUtilisateurActifById(id);
		if (utilisateur == null) {
			throw new AgrafException(ERREUR_UTILISATEUR_NOTEXIST);
		}
		// vérifie si l'utilisateur a des taches en cours
		final List<Tache> tacheList = tacheDAO.getTachesActiveByUtilisateurId(id);
		if (!CollectionUtils.isEmpty(tacheList)) {
			throw new AgrafException(ERREUR_UTILISATEUR_TACHE_ENCOURS);
		}

		utilisateur.setDateSortie(new Date());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#ajouterUtilisateur(fr.gfi.agraf
	 * .service.beans.UtilisateurDTO)
	 */
	public void ajouterUtilisateur(final UtilisateurDTO utilisateurDTO) throws AgrafException {
		final Utilisateur utilisateur = creerUtilisateurToAjouter(utilisateurDTO);
		utilisateurDAO.persist(utilisateur);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#associerUtilisateurProfil(fr
	 * .gfi.agraf.vue.bean.UtilisateurDTO)
	 */
	public void associerUtilisateurProfil(final UtilisateurDTO utilisateurDTO) throws AgrafException {
		// Récupere utilisateur actif
		final Utilisateur utilisateur = utilisateurDAO.getUtilisateurActifById(utilisateurDTO.getId());
		if (utilisateur == null) {
			throw new AgrafException(ERREUR_UTILISATEUR_NOTEXIST);
		}
		final List<Long> ids = new ArrayList<Long>();
		final List<ProfilDTO> listeProfilDTO = utilisateurDTO.getListeProfilDTO();
		if (!CollectionUtils.isEmpty(listeProfilDTO)) {
			for (final ProfilDTO profilDTO : listeProfilDTO) {
				ids.add(profilDTO.getId());
			}
		}
		// Récupere les profils en bdd
		final List<Profil> newProfils = profilDAO.getProfilsByIds(ids);
		// Vide la liste des profils actuel de l'utilisateur
		utilisateur.getProfils().clear();
		// Ajoute les nouveaux profils
		utilisateur.getProfils().addAll(newProfils);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * fr.gfi.agraf.service.IUtilisateurService#reactiverUtilisateurs(java.util
	 * .List)
	 */
	public void reactiverUtilisateurs(final List<Long> idUtilisateurs) throws AgrafException {
		final List<Utilisateur> utilisateurList = utilisateurDAO.getUtilisateursByIdList(idUtilisateurs);
		if (CollectionUtils.isEmpty(utilisateurList)) {
			throw new AgrafException(ERREUR_UTILISATEUR_NOTEXIST);
		}
		for (final Utilisateur utilisateur : utilisateurList) {
			utilisateur.setDateSortie(null);
			utilisateurDAO.update(utilisateur);
		}
	}

	/**
	 * Creer l'entity à ajouter
	 * 
	 * @param utilisateurDTO
	 * @return
	 * @throws AgrafException
	 */
	private Utilisateur creerUtilisateurToAjouter(final UtilisateurDTO utilisateurDTO) throws AgrafException {
		final Utilisateur utilisateur = new Utilisateur();
		utilisateur.setNom(utilisateurDTO.getNom());
		utilisateur.setPrenom(utilisateurDTO.getPrenom());
		utilisateur.setMotDePasse(CryptageUtils.crypterString(utilisateurDTO.getNewMotDePasse()));
		utilisateur.setDateEntree(new Date());
		verifieEtAjouteIdentifiant(utilisateur, utilisateurDTO.getNewIdentifiant());
		verifieEtAjouteMail(utilisateur, utilisateurDTO.getNewEmail());
		return utilisateur;
	}

	/**
	 * @return the utilisateurDAO
	 */
	public IUtilisateurDAO getUtilisateurDAO() {
		return utilisateurDAO;
	}

	/**
	 * @param utilisateurDAO
	 *            the utilisateurDAO to set
	 */
	public void setUtilisateurDAO(final IUtilisateurDAO utilisateurDAO) {
		this.utilisateurDAO = utilisateurDAO;
	}

	/**
	 * @return the tacheDAO
	 */
	public ITacheDAO getTacheDAO() {
		return tacheDAO;
	}

	/**
	 * @param tacheDAO
	 *            the tacheDAO to set
	 */
	public void setTacheDAO(final ITacheDAO tacheDAO) {
		this.tacheDAO = tacheDAO;
	}

}
